package com.jiangruan.javaproject.address;

import com.jiangruan.javaproject.utlis.Base58;
import com.jiangruan.javaproject.utlis.Hash;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/**
 * @author zhongjun
 * @ClassName com.jiangruan.javaproject.address.Address
 * @create 2021年12月25日 17:25
 * @desc:该类用于生成比特币的地址
 */
public class Address {
    /*
    * java中原生，生成一对密钥，并将密钥对象KeyPair返回
    * @return生成的密钥对
    * */
    public KeyPair generateKey(){
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");

            ECGenParameterSpec spec =new ECGenParameterSpec("secp256k1");

            generator.initialize(spec);


            return generator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }
    /*
    * 根据提供的密钥对数据，推导生成一个原始格式的比特币地址
    * @param  keyPair 提供 密钥对信息
    * return 推导生成的比特币地址
    * */
    public String newAddress(KeyPair keyPair){
        if (keyPair == null){
        return null;
        }
       ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
      //原始格式公钥;04 + x坐标（32字节）+ y坐标（32字节）
       ECPoint point= publicKey.getW();
       byte[] xBytes= point.getAffineX().toByteArray();
        byte[] yBytes= point.getAffineY().toByteArray();
       byte[] pubkey = toUncompressPubKey(xBytes,yBytes);

       byte[] sha256 = Hash.SHA256(pubkey);
       //公钥哈希
        byte[] pubHash =Hash.RIPEMD160(sha256);

        //添加前缀
        byte[] version =new byte[1];
        byte[] ver_pubhash = new byte[version.length+ pubHash.length];
        System.arraycopy(version,0,ver_pubhash,0,version.length);
       System.arraycopy(pubHash,0,ver_pubhash,1,pubHash.length);

       //双哈希，去校验位
        byte[] hash1 =Hash.SHA256(ver_pubhash);
        byte[] hash2 =Hash.SHA256(hash1);
        byte[] check =new byte[4];
        System.arraycopy(hash2,0,check,0,4);


        byte[] ver_pubhash_check =new  byte[ver_pubhash.length+check.length];
        System.arraycopy(ver_pubhash,0,ver_pubhash_check,0,ver_pubhash.length);
        System.arraycopy(check,0,ver_pubhash_check,ver_pubhash.length,check.length);

        //base58编码
        return Base58.encode(ver_pubhash_check);

    }
    /*
    校验比特币地址是否符合规范，并返回校验结果
    addr 要校验的字符串
    return 校验结果true表示符合规范，false表示不符合规范
     */
    public boolean validateAddr(String addr){
        if ("".equals(addr) || addr == null){
            return false;
        }
        byte[] ver_pubhash_check = Base58.decode(addr);
        if (ver_pubhash_check.length <4){
            return false;
        }
        byte[] check =new byte[4];
        System.arraycopy(ver_pubhash_check,ver_pubhash_check.length-4,check,0,4);

        byte[] ver_punhash = new byte[ver_pubhash_check.length-4];
        System.arraycopy(ver_pubhash_check,0,ver_punhash,0,ver_punhash.length);

       //双hash，取前4个字节
        byte[] hash1=Hash.SHA256(ver_punhash);
        byte[] hash2= Hash.SHA256(hash1);
        if (hash2 ==null){
            return false;
        }
        byte[] check_sum =new byte[4];
        System.arraycopy(hash2,0,check_sum,0,check_sum.length);

        //比较两个字节数组是否相等
        return Arrays.equals(check,check_sum);



    }

    /*
    该方法根据x坐标数据，用于拼接生成原始格式的公钥
    param x x坐标数据
    param y y坐标数据
    return 原始格式的公钥
     */
    private static byte[] toUncompressPubKey(byte[] x,byte[] y){
        //原始格式公钥;04 + x坐标（32字节）+ y坐标（32字节）
        byte[] publicKey = new byte[65];
        publicKey[0] =04;
        if (x.length == 32){
            //将x坐标全部拷贝到创建的原始格式公钥中去
            System.arraycopy(x,0,publicKey,1,x.length);
        }else  if(x.length >32){
            System.arraycopy(x,1,publicKey,1,32);
        }

        if (y.length == 32){
            System.arraycopy(y,0,publicKey,33,y.length);
        }else  if (y.length >32){
            System.arraycopy(y,1,publicKey,33,32);
        }
        return publicKey;
    }

}
